/*-------------------------------------------------------------------------
 *
 * cryptohash.c
 *	  加密哈希函数的后备实现。
 *
 * 这是在没有其他选择的情况下使用的核心功能集
 * 如OpenSSL。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/common/cryptohash.c
 *
 *-------------------------------------------------------------------------
 */

#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif

#include <sys/param.h>

#include "common/cryptohash.h"
#include "md5_int.h"
#include "sha1_int.h"
#include "sha2_int.h"

/*
 * 在后端，使用 palloc/pfree 来简化错误处理。在前端，
 * 使用 malloc 以便能够将失败状态返回给调用者。
 */
#ifndef FRONTEND
#define ALLOC(size) palloc(size)
#define FREE(ptr) pfree(ptr)
#else
#define ALLOC(size) malloc(size)
#define FREE(ptr) free(ptr)
#endif

/* 错误状态集 */
typedef enum pg_cryptohash_errno
{
	PG_CRYPTOHASH_ERROR_NONE = 0,
	PG_CRYPTOHASH_ERROR_DEST_LEN
} pg_cryptohash_errno;

/* 内部 pg_cryptohash_ctx 结构 */
struct pg_cryptohash_ctx
{
	pg_cryptohash_type type;
	pg_cryptohash_errno error;

	union
	{
		pg_md5_ctx	md5;
		pg_sha1_ctx sha1;
		pg_sha224_ctx sha224;
		pg_sha256_ctx sha256;
		pg_sha384_ctx sha384;
		pg_sha512_ctx sha512;
	}			data;
};

/*
 * pg_cryptohash_create
 *
 * 分配一个哈希上下文。 发生 OOM 时返回 NULL。 后端发出错误，而不返回。
 */
pg_cryptohash_ctx *
pg_cryptohash_create(pg_cryptohash_type fc_type)
{
	pg_cryptohash_ctx *fc_ctx;

	/*
	 * 请注意，这始终分配足够的空间以容纳最大的哈希。对于 md5、sha224 和 sha256，较小的分配就足够了，但多出的一点点内存并不值得使代码复杂化。
	 */
	fc_ctx = ALLOC(sizeof(pg_cryptohash_ctx));
	if (fc_ctx == NULL)
		return NULL;

	memset(fc_ctx, 0, sizeof(pg_cryptohash_ctx));
	fc_ctx->type = fc_type;
	fc_ctx->error = PG_CRYPTOHASH_ERROR_NONE;
	return fc_ctx;
}

/*
 * pg_cryptohash_init
 *
 * 初始化一个哈希上下文。请注意，这个实现旨在绝不失败，因此始终返回 0。
 */
int pg_cryptohash_init(pg_cryptohash_ctx *fc_ctx)
{
	if (fc_ctx == NULL)
		return -1;

	switch (fc_ctx->type)
	{
		case PG_MD5:
			pg_md5_init(&fc_ctx->data.md5);
			break;
		case PG_SHA1:
			pg_sha1_init(&fc_ctx->data.sha1);
			break;
		case PG_SHA224:
			pg_sha224_init(&fc_ctx->data.sha224);
			break;
		case PG_SHA256:
			pg_sha256_init(&fc_ctx->data.sha256);
			break;
		case PG_SHA384:
			pg_sha384_init(&fc_ctx->data.sha384);
			break;
		case PG_SHA512:
			pg_sha512_init(&fc_ctx->data.sha512);
			break;
	}

	return 0;
}

/*
 * pg_cryptohash_update
 *
 * 更新哈希上下文。请注意，这个实现旨在绝不失败，因此在调用者未提供 NULL 上下文时，始终返回 0。
 */
int pg_cryptohash_update(pg_cryptohash_ctx *fc_ctx, const uint8 *fc_data, size_t fc_len)
{
	if (fc_ctx == NULL)
		return -1;

	switch (fc_ctx->type)
	{
		case PG_MD5:
			pg_md5_update(&fc_ctx->data.md5, fc_data, fc_len);
			break;
		case PG_SHA1:
			pg_sha1_update(&fc_ctx->data.sha1, fc_data, fc_len);
			break;
		case PG_SHA224:
			pg_sha224_update(&fc_ctx->data.sha224, fc_data, fc_len);
			break;
		case PG_SHA256:
			pg_sha256_update(&fc_ctx->data.sha256, fc_data, fc_len);
			break;
		case PG_SHA384:
			pg_sha384_update(&fc_ctx->data.sha384, fc_data, fc_len);
			break;
		case PG_SHA512:
			pg_sha512_update(&fc_ctx->data.sha512, fc_data, fc_len);
			break;
	}

	return 0;
}

/*
 * pg_cryptohash_final
 *
 * 最终化哈希上下文。请注意，这个实现旨在绝不失败，因此在目标缓冲区不够大的情况下，始终返回 0。
 */
int pg_cryptohash_final(pg_cryptohash_ctx *fc_ctx, uint8 *fc_dest, size_t fc_len)
{
	if (fc_ctx == NULL)
		return -1;

	switch (fc_ctx->type)
	{
		case PG_MD5:
			if (fc_len < MD5_DIGEST_LENGTH)
			{
				fc_ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
				return -1;
			}
			pg_md5_final(&fc_ctx->data.md5, fc_dest);
			break;
		case PG_SHA1:
			if (fc_len < SHA1_DIGEST_LENGTH)
			{
				fc_ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
				return -1;
			}
			pg_sha1_final(&fc_ctx->data.sha1, fc_dest);
			break;
		case PG_SHA224:
			if (fc_len < PG_SHA224_DIGEST_LENGTH)
			{
				fc_ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
				return -1;
			}
			pg_sha224_final(&fc_ctx->data.sha224, fc_dest);
			break;
		case PG_SHA256:
			if (fc_len < PG_SHA256_DIGEST_LENGTH)
			{
				fc_ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
				return -1;
			}
			pg_sha256_final(&fc_ctx->data.sha256, fc_dest);
			break;
		case PG_SHA384:
			if (fc_len < PG_SHA384_DIGEST_LENGTH)
			{
				fc_ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
				return -1;
			}
			pg_sha384_final(&fc_ctx->data.sha384, fc_dest);
			break;
		case PG_SHA512:
			if (fc_len < PG_SHA512_DIGEST_LENGTH)
			{
				fc_ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN;
				return -1;
			}
			pg_sha512_final(&fc_ctx->data.sha512, fc_dest);
			break;
	}

	return 0;
}

/*
 * pg_cryptohash_free
 *
 * 释放哈希上下文。
 */
void pg_cryptohash_free(pg_cryptohash_ctx *fc_ctx)
{
	if (fc_ctx == NULL)
		return;

	explicit_bzero(fc_ctx, sizeof(pg_cryptohash_ctx));
	FREE(fc_ctx);
}

/*
 * pg_cryptohash_error
 *
 * 返回一个静态字符串，提供有关计算过程中发生的错误的详细信息。
 */
const char * pg_cryptohash_error(pg_cryptohash_ctx *fc_ctx)
{
	/*
	 * 此实现永远不会因内存不足错误而失败，
	 * 除非在创建上下文时。
	 */
	if (fc_ctx == NULL)
		return _("out of memory");

	switch (fc_ctx->error)
	{
		case PG_CRYPTOHASH_ERROR_NONE:
			return _("success");
		case PG_CRYPTOHASH_ERROR_DEST_LEN:
			return _("destination buffer too small");
	}

	Assert(false);
	return _("success");
}
